// 实现这个项目的构建任务

// gulp官方网站api：https://www.gulpjs.com.cn/docs/api/dest/

// src 为gulp中方法：创建一个流，用于从文件系统读取 Vinyl 对象。
// dest为gulp中方法：创建一个用于将 Vinyl 对象写入到文件系统的流。
const { src, dest, parallel, series, watch } = require('gulp') // npm install gulp --save-dev
const del = require('del') // npm install --save-dev del

// const sass = require('gulp-sass') // npm install gulp-sass --save-dev
// const babel = require('gulp-babel') // npm install gulp-babel --save-dev
// const swig = require('gulp-swig') // npm install gulp-swig --save-dev
// const imagemin = require('gulp-imagemin') // npm install gulp-imagemin --save-dev

// 自动加载全部的plugin；例如sass，使用plugins.sass替换即可
const loadPlugins = require('gulp-load-plugins') // npm install gulp-load-plugins --save-dev
const plugins = loadPlugins() 

// web服务插件，可实现热更新
const browserSync = require('browser-sync')// npm install browser-sync --save-dev
const bs = browserSync.create()

// 获取执行项目的根路径
const cwd = process.cwd()



// 模板引擎中需要用到的数据
let config = {
    // default config
    build: {
      src: 'src',
      dist: 'dist',
      temp: 'temp',
      public: 'public',
      paths: {
        styles: 'assets/styles/*.scss',
        scripts: 'assets/scripts/*.js',
        pages: '*.html',
        images: 'assets/images/**',
        fonts: 'assets/fonts/**'
      }
    }
}

try {
    const loadConfig = require(`${cwd}/pages.config.js`)
    config = Object.assign({}, config, loadConfig)
} catch (e) {}




/**
 * 以下为各种任务的创建代码
 * develop：js、css、page在develop阶段生成到temp目录
 * build：js、css、page在build后压缩重新挪到release目录，图片、字体、其它文件不做处理直接放到release目录
 */


// ==========> 
// 清空release和tmp目录
// 依赖的插件有：
// 1、del
const clean = () => {
    return del(['release','temp'])
}
  

// sass文件的转换
// styles目录中_开头的scss文件默认不会处理
// 依赖的插件有：
// 1、gulp-sass
const style = () => {
    return src('src/assets/styles/*.scss',{ base: 'src'}) // base指定基础目录，转换后release目录才会和src保持一样的结构
        .pipe(plugins.sass({ outputStyle: 'expanded'})) // { outputStyle: 'expanded'} 参数可以指定结束括号格式化    
        .pipe(dest('temp'))
}

// es6+代码的转换
// 依赖的插件有：
// 1、gulp-babel => babel工具：转换平台，不作具体的工作
// 2、@babel/core => babel核心转换模块：完成语言转换工作
// 3、@babel/preset-env => babel对应的转换插件：指定babel转换内容，指定es6+全部新特性的功能转换到es5
const script = () => {
    return src('src/assets/scripts/*.js',{ base: 'src'})
        .pipe(plugins.babel({ 
            presets: ['@babel/preset-env'] // 编译es6，必须传递参数preset-env，否则转换失效(babel配置可以单独放到.babelrc文件)
        }))
        .pipe(dest('temp'))
}

// html文件的转换
// 依赖的插件有：
// 1、gulp-swig => 模板引擎
const page = () => {
    return src('src/**/*.html',{ base: 'src'}) // 其中/**/*.html表示在src目录下任意子目录下的匹配
        .pipe(plugins.swig({ data: config.data, defaults: { cache: false }  }))   //转换模板页，将data数据传递给模板使用，cache：swig缓存设置，否则热更新时可能不立即生效
        .pipe(dest('temp'))
}


// ==========> 以下三个任务，上线前build任务里面执行即可
// 图片的处理
// 依赖的插件有：
// 1、gulp-imagemin => 压缩图片
const image = () => {
    return src('src/assets/images/**',{ base: 'src'})
        .pipe(plugins.imagemin())
        .pipe(dest('release'))
}

// 字体文件，拷贝
// 其中有svg，也按照图片的处理方式压缩
const font = () => {
    return src('src/assets/fonts/**', { base: 'src' })
      .pipe(plugins.imagemin())
      .pipe(dest('release'))
}

// 其它文件的任务
const extra = () => {
    return src('public/**', { base: 'public' })
        .pipe(dest('release'))
}


// ==========> 
// 热更新配置
// 模拟启动一个Web服务：
// 1、监视src文件，重新构建
// 2、监视release，更新浏览器
const serve = () => {
    // 监视src文件，重新构建代码
    watch('src/assets/styles/*.scss', style)
    watch('src/assets/scripts/*.js', script)
    watch('src/*.html', page)
    // watch('src/assets/images/**', image)
    // watch('src/assets/fonts/**', font)
    // watch('public/**', extra)

    // 图片、字体在开发阶段没有必要重新执行构建任务；所以让gulp从源文件src、public里面查找  
    // 图片、字体等文件发生变化后，bs.reload 更新浏览器
    watch([
        'src/assets/images/**',
        'src/assets/fonts/**',
        'public/**'
    ], bs.reload)
    
    // 配置web服务器，监视release
    bs.init({
        notify: false, // 右上角提示
        port: 2080, // 服务的端口，默认300
        // open: false, // 自动打开浏览器
        files: 'temp/**', // 热更新，temp下的所有文件变化时，同步更新浏览器
        server: {
          //服务启动时，首先访问temp根目录；查询路径：temp=>src=>public
          //因为图片字体等文件没必要热更新重新构建（减少构建时间），所以让gulp从源文件src、public里面查找  
          baseDir: ['temp', 'src', 'public'], 
          routes: {// 路径查找路由
            '/node_modules': 'node_modules' //优先级高于baseDir,先查找node_modules目录
          }
        }
    })
}

// 执行速度较慢，建议上线前才使用，开发中不使用；
// 依赖插件：
// 1、gulp-useref => 查询路径，合并文件
// 2、gulp-if => 判断条件插件
// 3、gulp-uglify => 压缩js
// 4、gulp-clean-css => 压缩css
// 5、gulp-htmlmin => 压缩html
const useref = () => {
    return src('temp/*.html', { base: 'temp' })
        .pipe(plugins.useref({ searchPath: ['temp', '.'] })) //查询路径：temp、.根目录下（node_modules） 
        // html js css压缩代码
        .pipe(plugins.if(/\.js$/, plugins.uglify()))
        .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
        .pipe(plugins.if(/\.html$/, plugins.htmlmin({
            collapseWhitespace: true, // 折叠空白字符
            minifyCSS: true, // 自动压缩style里面的css
            minifyJS: true // 自动压缩script里面的js
            // 还有删除注释、空属性等配置
            // ...
        })))
        .pipe(dest('release')) //将最后合并压缩的所有文件移到release目录
}
  








/**
 * 以下整理各种模式下调用的任务
 */

// 代码任务：只构建各种代码
const compile = parallel(style, script, page) // parallel组合各种小任务， parallel是并行执行、series是串行执行


// 1、发布模式：执行所有任务；先删除目录下的文件，再build
// 2、发布前需要压缩文件，然后将图片、文件也处理了
const build = series(
    clean, 
    parallel(
        series(compile, useref), 
        extra, 
        image, 
        font
    )
)


// 2、开发模式：只构建各种代码，启动web服务，图片/字体等文件使用src本地加快构建速度
const develop = series(clean, compile, serve)



module.exports = {
    clean,
    build,
    develop
}